#!/bin/bash
#
# Rename subtitles (.srt files) to match the given video files
#
# Copyright (C) 2011 Rodrigo Silva (MestreLion) <linux@rodrigosilva.com>
# License: GPLv3 or later, at your choice. See <http://www.gnu.org/licenses/gpl>
#
# TODO: Sort files array
# TODO: Support for multiple language subtitles ( *.xx.srt )
# FIXME: Decent usage explaining options and behaviour

self="${0##*/}"

fatal() {
	local msg="$1"
	[[ "$msg" ]] && printf "%s: %s\nTry '%s --help' for more information\n" \
	                       "$self" "$msg" "$self" >&2
	exit 1
}
usage() {
cat << USAGEDOC
Rename subtitles (.srt files) to match the given video files

Usage: $self [--undo FILE] [--verbose] [--run] [--] [FILE|DIR]...

Options:
--undo FILE - Create an undo script to revert the renaming. Run ./FILE to revert
--run       - If not used, will not actually rename subtitles, only show what will be done

Video files are *.avi, *.mkv, *.mpg and *.mpeg. All other files are ignored
Files that already have a matching subtitle and subtitles that already have a matching video will be ignored
For desired results, files in same dir must be given in alphabetical order (use globs for that).
Example: $self ~/Videos/Lost /shared/csi/miami_s1* '/movies/star wars'
USAGEDOC
exit
}

printundo() {
	
	local header="$1"
	local oldfile="$2"
	local newfile="$3"
	
	if [[ -z "$header" ]]; then
		printf '#Automatically generated by %s at %s\n' "$self" "$(date +'%F %T')"
		printf '#arguments:'
		printf ' %q' "${args[@]}"
		printf '\ncd %q\n' "$PWD"
	fi
	
	printf "mv -- %q %q\n" "$oldfile" "$newfile"
}

addfiles() {
	local arg="$1"
	[[ -f "$arg" ]] && { files+=( "$arg"       ) ; return ; } # add file
	[[ -d "$arg" ]] && { files+=( "${arg%/}/"* ) ; return ; } # add files in dir
	fatal "\'$arg\' is not a file or directory"
}

#save for log
args=( "$@" )

# command line parsing
while [[ $# -gt 0 ]]; do
	arg="$1"; shift
	case "$arg" in
	-h|--help   ) usage           ;;
	-t|--run    ) run=1           ;;
	-v|--verbose) verbose=1       ;;
	-u|--undo   ) undo="$1";shift ;;
	--          ) break           ;;
	*           ) addfiles "$arg" ;;
	esac
done
for arg; do addfiles "$arg"; done # add remaining files after --
[[ "${#files[@]}" -gt 0 ]] || addfiles "$PWD" # no files given: add current dir

# loop the files
prevdir=""
match=""
for video in "${files[@]}"; do

	# File exists? (can be an umatched glob or empty dir)
	[[ -f "$video" ]] || continue

	# Split full filename in file, title, ext and dir (with trailing '/')
	file="${video##*/}"
	title="${file%.*}"
	ext="${file#"$title"}"
	ext="${ext#.}"
	dir="${video%"$file"}"
	dir="${dir:-./}"

	# Is it a video?
	case "${ext,,}" in avi|mpg|mpeg|mkv|mp4) ;; *) continue ;; esac

	# Has a matching subtitle already?
	subtitle="${video%."$ext"}.srt"
	[[ -f "$subtitle" ]] && continue

	# Populate the unmatched subtitles array for this dir
	if [[ "$dir" != "$prevdir" ]]; then
		prevdir="$dir"
		allsubs=( "${dir%/}"/*.[Ss][Rr][Tt] )
		subtitles=()
		s=0
		for sub in "${allsubs[@]}"; do
			if [[ -f "$sub" ]]; then
				matchsub=""
				for f in "${sub%.[Ss][Rr][Tt]}".{avi,mpg,mpeg,mkv,mp4}; do
					if [[ -f "$f" ]]; then matchsub=1; break; fi
				done
				[[ "$matchsub" ]] || subtitles+=( "$sub" )
			fi
		done
	fi
	
	# Are there any avaliable, unmatched subtitles left in this dir?
	(( s < ${#subtitles[@]} )) || continue
	
	# Rename the first subtitle
	[[ "$verbose" ]] && printf 'old=%s\tnew=%s\n' "${subtitles[s]}" "${subtitle}"
	[[ "$undo" ]] && printundo "$match" "${subtitle}" "${subtitles[s]}" >> "$undo" || fatal "could not write to undo file $undo"
	[[ "$run" ]] && { mv -- "${subtitles[s]}" "${subtitle}" || fatal "could not rename ${subtitles[s]}" ; }
	match=1
	(( s++ ))
	
done

if [[ "$undo" && "$match" ]]; then
	printf '\n' >> "$undo" || fatal "could not write to undo file $undo"
	chmod +x "$undo" || fatal "could not set to executable undo script $undo"
fi

exit 0
